<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>函数</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="typeconv-oper.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="typeconv.html">快退</a></td><td width="60%" align="center" valign="bottom">章10. 类型转换</td><td width="10%" align="right" valign="top"><a href="typeconv.html">快进</a></td><td width="10%" align="right" valign="top"><a href="typeconv-query.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="TYPECONV-FUNC">10.3. 函数</a></h1><a name="AEN15762"></a>
<p>下面讲解的过程解释了如何在一次函数调用中确定所使用的究竟是哪个函数。</p>
<div class="PROCEDURE">
<p><b>函数类型解析</b></p>
<ol type="1">
<li class="STEP"><p>从系统表 <code class="CLASSNAME">pg_proc</code> 中选择要考虑的函数。如果使用了一个不带修饰的函数名字，那么认为该函数是那些在当前搜索路径中名字和参数个数都正确的函数(参阅<a href="ddl-schemas.html#DDL-SCHEMAS-PATH">节5.7.3</a>)。如果给出一个带修饰的函数名，那么只考虑指定模式中的函数。</p>
<ol class="SUBSTEPS" type="a">
<li class="STEP"><p>如果搜索路径中找到了多个相同参数类型的函数，那么只考虑最早出现在路径中的那一个。但是不同参数类型的函数将被平等看待，而不管它们在路径中的位置如何。</p></li>
</ol></li>
<li class="STEP"><p>查找精确接受输入参数类型的函数。如果找到一个(在一组被考虑的函数中，可能只存在一个精确匹配的)，则用之。包含 <tt class="TYPE">unknown</tt> 类型的函数调用绝不会在此处找到匹配。</p></li>
<li class="STEP"><p>如果没有找到精确的匹配，则看看函数调用是否需要一个类型转换。如果函数调用只有一个参数并且函数名与某些数据类型的内部名称相同，那么就会出现这种情况。另外，该函数的参数必须是一个未知类型的文本或者与某个已命名数据类型二进制兼容。如果符合这些条件，则该函数的参数在不做任何实际函数调用的情况下转换成这个已命名的数据类型。</p></li>
<li class="STEP"><p>寻找最优匹配。</p>
<ol class="SUBSTEPS" type="a">
<li class="STEP"><p>抛弃那些输入类型不匹配并且也不能隐式转换成匹配的候选函数。<tt class="TYPE">unknown</tt> 文本在这种情况下可以转换成任何东西。如果只剩下一个候选项，则用之，否则继续下一步。</p></li>
<li class="STEP"><p>遍历所有候选函数，保留那些输入类型匹配最准确的。此时，域被看作和他们的基本类型相同。如果没有一个函数能被保留，则保留所有候选。如果只剩下一个候选项，则用之，否则继续下一步。</p></li>
<li class="STEP"><p>遍历所有候选函数，保留那些需要类型转换时接受(属于输入数据类型的类型范畴的)首选类型位置最多的函数。如果没有接受首选类型的函数，则保留所有候选。如果只剩下一个候选项，则用之，否则继续下一步。</p></li>
<li class="STEP"><p>如果有任何输入参数是 <tt class="TYPE">unknown</tt> 类型，检查剩余的候选函数对应参数位置的类型范畴。在每一个能够接受字符串类型范畴的位置使用 <tt class="TYPE">string</tt> 类型(这种对字符串的偏爱是合适的，因为 unknown 文本确实像字符串)。另外，如果所有剩下的候选函数都接受相同的类型范畴，则选择该类型范畴，否则抛出一个错误(因为在没有更多线索的条件下无法作出正确的选择)。现在抛弃不接受选定的类型范畴的候选函数，然后，如果任意候选函数在某个给定的参数位置接受一个首选类型，则抛弃那些在该参数位置接受非首选类型的候选函数。</p></li>
<li class="STEP"><p>如果只剩下一个函数，则用之。如果还有多个候选函数或者没有候选函数，则产生一个错误。</p></li>
</ol></li>
</ol>
</div>
<p>请注意，"最佳匹配"规则对操作符和对函数的类型分析都是一样的。下面是一些例子。</p>
<div class="EXAMPLE"><a name="AEN15798"></a>
<p><b>例10-4. 圆整函数参数类型解析</b></p>
<p>只有一个 <code class="FUNCTION">round</code> 函数有两个参数(第一个是 <tt class="TYPE">numeric</tt> ，第二个是 <tt class="TYPE">integer</tt>)。所以下面的查询自动把第一个类型为 <tt class="TYPE">integer</tt> 的参数转换成 <tt class="TYPE">numeric</tt> 类型：</p>
<pre class="SCREEN">SELECT round(4, 4);

 round
--------
 4.0000
(1 row)</pre>
<p>实际上它被分析器转换成：</p>
<pre class="SCREEN">SELECT round(CAST (4 AS numeric), 4);</pre>
<p>因为带小数点的数值常量初始时被赋予 <tt class="TYPE">numeric</tt> 类型，因此下面的查询将不需要类型转换，并且可能会略微高效一些：</p>
<pre class="SCREEN">SELECT round(4.0, 4);</pre>
</div>
<div class="EXAMPLE"><a name="AEN15811"></a>
<p><b>例10-5. 子字符串函数类型解析</b></p>
<p>有好几个 <code class="FUNCTION">substr</code> 函数，其中一个接受 <tt class="TYPE">text</tt> 和 <tt class="TYPE">integer</tt> 类型。如果用一个未声明类型的字符串常量调用它，系统将选择接受 <tt class="LITERAL">string</tt> 类型范畴的首选类型(也就是 <tt class="TYPE">text</tt> 类型)的候选函数。</p>
<pre class="SCREEN">SELECT substr('1234', 3);

 substr
--------
     34
(1 row)</pre>
<p>如果该字符串声明为 <tt class="TYPE">varchar</tt> 类型，就像从表中取出来的数据一样，分析器将试着将其转换成 <tt class="TYPE">text</tt> 类型：</p>
<pre class="SCREEN">SELECT substr(varchar '1234', 3);

 substr
--------
     34
(1 row)</pre>
<p>被分析器转换后实际上变成：</p>
<pre class="SCREEN">SELECT substr(CAST (varchar '1234' AS text), 3);</pre>
<div class="NOTE">
<blockquote class="NOTE">
<p><b>【注意】</b>分析器从 <tt class="STRUCTNAME">pg_cast</tt> 表中了解到 <tt class="TYPE">text</tt> 和 <tt class="TYPE">varchar</tt> 是二进制兼容的，意思是说一个可以传递给接受另一个的函数而不需要做任何物理转换。因此，在这种情况下，实际上没有做任何明确的类型转换。</p>
</blockquote>
</div>
<p>而且，如果以 <tt class="TYPE">integer</tt> 为参数调用函数，分析器将试图将其转换成 <tt class="TYPE">text</tt> 类型：</p>
<pre class="SCREEN">SELECT substr(1234, 3);

 substr
--------
     34
(1 row)</pre>
<p>实际上是这样执行的：</p>
<pre class="SCREEN">SELECT substr(CAST (1234 AS text), 3);</pre>
<p>这种自动转换能够成功是因为存在一个从 <tt class="TYPE">integer</tt> 到 <tt class="TYPE">text</tt> 的隐含转换可以调用。</p>
</div>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="typeconv-oper.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="typeconv-query.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">操作符</td><td width="34%" align="center" valign="top"><a href="typeconv.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">值存储</td></tr>
</table>
</div>
</body></html>